// =============================================================================
//
//  Name: VirtualLCD.c
//  Copyright: Dave Perrow
//  Author: Dave Perrow
//  Date: 09/07/11 18:09
//  Description: LCD martie display plugin
//               Creates a borderless, title less window and displays the 
//               4 lines of info in it. Allows setting of window position,
//               size, background, line positins and font details from a
//               configuration file.
//
// =============================================================================
#define WIN32_LEAN_AND_MEAN
#include <windef.h>
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <wingdi.h>
#include <olectl.h>
#include <time.h>

#include "VirtualLCD.h"
#include "VirtualLCD_private.h"
//
// Maximum nr. characters and lines supported
//
#define DISPLAY_WIDTH   80          // maximum of 80 characters per line
#define DISPLAY_HEIGHT  4           // maximum of 4 lines
#define HIMETRIC_INCH	2540        // used to convert inches to pixels
//
// Character map to select Icons
//
struct CHARMAP {
        int used;
        int loaded;
        long width,height;
        VLCD_BYTE px[8];
        char fname[256];
        } charmap[MAX_CMATCH] = {
                              1,0,0,0, 16,24,28,30,28,24,16, 0, "images\\play.gif",
                              2,0,0,0, 31,31,31,31,31,31,31, 0, "images\\stop.gif",
                              3,0,0,0, 27,27,27,27,27,27,27, 0, "images\\pause.gif",
                              4,0,0,0,  0,20,22,23,23,22,20, 0, "images\\next.gif",
                              5,0,0,0,  0, 5,13,29,29,13, 5, 0, "images\\prev.gif",
                              6,0,0,0,  0, 4,14,31,31,14, 4, 0, "images\\recording.gif",
                              7,0,0,0,  0,20,26,29,29,26,20, 0, "images\\fast_forward.gif",
                              8,0,0,0,  0, 5,11,23,23,11, 5, 0, "images\\rewind.gif",
                              9,0,0,0,  0,20,22,23,23,22,20, 0, "images\\next_frame.gif",
                             10,0,0,0,  0, 5,13,29,29,13, 5, 0, "images\\prev_frame.gif",
                             11,0,0,0, 11,11,11,27,11,11,11, 0, "images\\slow_motion.gif",
                             12,0,0,0,  8,13,10,15,11,26,12, 8, "images\\mute_on.gif",
                             13,0,0,0,  8,12,10,11,11,10,12, 8, "images\\mute_off.gif",
                             14,0,0,0,  0,31,21, 4, 4, 4, 4, 0, "images\\phone_call.gif",
                              0,0,0,0,  0,0,0,0,0,0,0,0, "unused.jpg",
                              0,0,0,0,  0,0,0,0,0,0,0,0, "unused.jpg",
                              0,0,0,0,  0,0,0,0,0,0,0,0, "unused.jpg",
                              0,0,0,0,  0,0,0,0,0,0,0,0, "unused.jpg",
                              0,0,0,0,  0,0,0,0,0,0,0,0, "unused.jpg",
                              0,0,0,0,  0,0,0,0,0,0,0,0, "unused.jpg"                              
};
LPPICTURE charpictures[MAX_CMATCH];

//
// external function definitions
//
extern int __stdcall read_cfg_file();
extern int __stdcall mylog(char * module, char * msg);
extern int __stdcall open_mylog();
extern int __stdcall close_mylog();
extern int __stdcall mylogi(char * module, char * msg);
extern int __stdcall mylogw(char * module, UINT msg);
extern int __stdcall mylog_error(char *msg);
extern int __stdcall get_win_vals(int *x,int *y,int *width,int *height, SCREENDEF screen[4]);
extern int __stdcall pDontShowWindow(void);
extern int __stdcall pRecord(void);
extern int __stdcall pRecordInterval(void);
extern char * __stdcall pRecordFile(void);



void putcharmapfname(int entry,char *fname) 
{ char str[256]; sprintf(str,"entry=%d, fname=%s",entry,fname); mylog("putcharmapfname",str);
  if ((entry>0) && (entry<MAX_CMATCH)) strcpy(charmap[entry-1].fname,fname); 
}
void putcharmapchars(int entry,VLCD_BYTE chars[]) 
{ int i; char str[20]; 
  sprintf(str,"entry=%d, chars=%d,%d,%d,%d,%d,%d,%d,%d",entry,chars[0],chars[1],chars[2],chars[3],
                    chars[4],chars[5],chars[6],chars[7]); mylog("putcharmapchars",str);
  if ((entry>0) && (entry<MAX_CMATCH))
    { charmap[entry-1].used=entry; for (i=0;i<8;i++) charmap[entry-1].px[i]=chars[i]; } 
}

void resetcharmap() { int i; for (i=0;i<MAX_CMATCH;i++) charmap[i].used=0; }

HFONT GetAFont(int f1, int f2, int f3, long height, long width, int FontType, char *FontName);
int display_screen(void);
//
//  Declare Windows procedure 
//
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
//
// static variables
//
// structure to hold the display contents (and the previous contents
//
static int display_active=0;                     // whether the window has been created or closed
static int display_changed=0;                    // whether something has changed the display
struct display_line { char line [DISPLAY_WIDTH]; } display [DISPLAY_HEIGHT] = {" "," "," "," "};
struct display_line last_display[DISPLAY_HEIGHT] = {" "," "," "," "};
//
int my_x,my_y;                                   // Current position on character display
static char szClassName[ ] = "VirtualLCD";             // Name of this class
static HINSTANCE hThisInstance;                  // value of current instance
static HWND hwnd;                                // This is the handle for our window 
static HDC hdc;                                  // Device Context for the window
static MSG messages;                             // Here messages to the application are saved 
WNDCLASSEX wincl;                                // Data structure for the windowclass 
LPPICTURE BackgpPicture;                             // Pointer for background picture
static long BackhmWidth;
static long BackhmHeight;

static double CaptureCounter=0;



struct CustomChar { VLCD_BYTE ln[8]; } CustomChars[9] = { 0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          0,0,0,0,0,0,0,0,
                                                          255,255,255,255,255,255,255,255 
                                                        };
//VLCD_BYTE bitnum[8]={ 1,2,4,8,16,32,64,128 };
VLCD_BYTE bitnum[8]={ 16,8,4,2,1,0,0,0 }; // only 5 bits used
extern void __stdcall OutputALine(int i, HDC mhdc);
SCREENDEF myscreen[4];

// =============================================================================
//
// This function Captures an image of the window.
//
// =============================================================================
int CaptureAnImage(HWND hWnd)
{
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcWindow = GetDC(hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow); 
    if(!hdcMemDC) { mylog("CaptureAnImage","CreateCompatibleDC has failed"); }
    else
     { // Get the client area for size calculation
       RECT rcClient;
       GetClientRect(hWnd, &rcClient);
 
       // Create a compatible bitmap from the Window DC
       hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
       if(!hbmScreen) { mylog("CaptureAnImage","CreateCompatibleBitmap has failed"); }
       else
        { // Select the compatible bitmap into the compatible memory DC.
          SelectObject(hdcMemDC,hbmScreen);
          // Bit block transfer into our compatible memory DC.
          if(!BitBlt(hdcMemDC, 0,0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, hdcWindow, 0,0,SRCCOPY))
           {   mylog("CaptureAnImage","BitBlt has failed"); }
          else
           {  GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);   // Get the BITMAP from the HBITMAP
     
              BITMAPFILEHEADER   bmfHeader;    
              BITMAPINFOHEADER   bi;
              bi.biSize = sizeof(BITMAPINFOHEADER);    
              bi.biWidth = bmpScreen.bmWidth;    
              bi.biHeight = bmpScreen.bmHeight;  
              bi.biPlanes = 1;    
              bi.biBitCount = 32;    
              bi.biCompression = BI_RGB;    
              bi.biSizeImage = 0;  
              bi.biXPelsPerMeter = 0;    
              bi.biYPelsPerMeter = 0;    
              bi.biClrUsed = 0;    
              bi.biClrImportant = 0;

              DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

              // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
              // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
              // have greater overhead than HeapAlloc.
              HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize); 
              char *lpbitmap = (char *)GlobalLock(hDIB);    

              // Gets the "bits" from the bitmap and copies them into a buffer 
              // which is pointed to by lpbitmap.
              GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
              // A file is created, this is where we will save the screen capture.
              mylog("Capture",pRecordFile());
              HANDLE hFile = CreateFile(pRecordFile(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);   
              // Add the size of the headers to the size of the bitmap to get the total file size
              DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
              //Offset to where the actual bitmap bits start.
              bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); 
              bmfHeader.bfSize = dwSizeofDIB;    //Size of the file
              bmfHeader.bfType = 0x4D42;         //BM //bfType must always be BM for Bitmaps  
              DWORD dwBytesWritten = 0;
              WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
              WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
              WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);  
                     
              GlobalUnlock(hDIB);               //Unlock and Free the DIB from the heap
              GlobalFree(hDIB);
              CloseHandle(hFile);               //Close the handle for the file that was created
           }
         }
       }
    //Clean up
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    ReleaseDC(hWnd,hdcWindow);

    return 0;
}




// =============================================================================
//
// This function loads a file into an IStream.
//
// =============================================================================
void LoadPictureFile(char *fnam, LPPICTURE* gpPicture, long *Width, long *Height)
{	
    LPVOID pvData = NULL;
	DWORD dwBytesRead = 0;
	LPSTREAM pstm = NULL;
    	
	// open file
	HANDLE hFile = CreateFile(TEXT(fnam), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hFile==INVALID_HANDLE_VALUE) mylog_error("LoadPicture CreateFile failed");
    mylog("LoadPicture","File Open");
    
	// get file size
	DWORD dwFileSize = GetFileSize(hFile, NULL);
    if (dwFileSize==(-1)) mylog_error("LoadPicture GetFileSize failed");
    mylog("LoadPicture","Got file size");

	// alloc memory based on file size
	HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
    if (hGlobal==NULL) mylog_error("LoadPicture GlobalAlloc failed");
    mylog("LoadPicture","Allocated memory");

	pvData = GlobalLock(hGlobal);
    if (pvData==NULL) mylog_error("LoadPicture GlobalLock failed");

	// read file and store in global memory
	BOOL bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL);
    if (bRead==FALSE) mylog_error("LoadPicture ReadFile failed");
	GlobalUnlock(hGlobal);
	CloseHandle(hFile);

	// create IStream* from global memory
	HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
    if (hr!=S_OK) mylog_error("LoadPicture CreateStreamOnHGlobal failed");

	// Create IPicture from image file
	if (*gpPicture) (*gpPicture)->Release();
	hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)gpPicture);
    if (hr!=S_OK) mylog_error("LoadPicture CreateStreamOnHGlobal failed");
    if (!gpPicture) mylog_error("LoadPicture CreateStreamOnHGlobal failed 2");
    (*gpPicture)->get_Width(Width);
    (*gpPicture)->get_Height(Height);
	pstm->Release();
    mylog("LoadPicture","Appears to have loaded");
}




void display_picture(HDC mhdc, LPPICTURE *gpPicture, RECT *irc, long x, long y, long Width, long Height, long fWidth, long fHeight)
{ char str[256];
  RECT rc;	
  int nWidth,nHeight;
  long myx,myy;

  myx=x; myy=y;
  rc.left=(*irc).left; rc.right=(*irc).right; rc.top=(*irc).top; rc.bottom=(*irc).bottom;
  // convert himetric to pixels
    if (Width==0)
      {  nWidth	= MulDiv(fWidth, GetDeviceCaps(hdc, LOGPIXELSX), HIMETRIC_INCH);
         nHeight = MulDiv(fHeight, GetDeviceCaps(hdc, LOGPIXELSY), HIMETRIC_INCH);
      }
    else { nWidth=Width; nHeight=(Height-(Height/5)); }          // reduce by 20%

//  sprintf(str,"hmWidth=%d hmHeight=%d, nWidth=%d, nHeight=%d, left=%d,%d, right=%d,%d",
//           Width,Height,nWidth,nHeight,rc.left, rc.top, rc.right, rc.bottom);
//  mylog("about to render",str);
  // display picture using IPicture::Render
  if (mhdc==NULL) mylog("Render","mhdc is NULL");
//  (*gpPicture)->Render(mhdc, x, y, (LONG)nWidth, (LONG)nHeight, (LONG)0, fHeight, fWidth, -fHeight, &rc);
  (*gpPicture)->Render(mhdc, myx, myy, (LONG)nWidth, (LONG)nHeight, (LONG)0, fHeight, fWidth, -fHeight, &rc);
//  mylog("rendered",str);

}



// =============================================================================
//
// Function to create the window
//
// =============================================================================
int create_screen()
{
  int x,y,width,height;
  char background[256];
  SCREENDEF myscreen[4];

  /* The Window structure */
  mylog("create_screen","entered");
  if (display_active==0)
   {
    hThisInstance=GetModuleHandle("VirtualLCD");
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default color as the background of the window */
    wincl.hbrBackground = (HBRUSH) NULL;       // So we get no background painting
    /* Register the window class, and if it fails tht's okay */
    if (!RegisterClassEx (&wincl)) { mylog_error("Error from RegisterClassEx"); }


    // get the window params from the ini file
    get_win_vals(&x,&y,&width,&height,myscreen);
    LoadPictureFile("displays\\VirtualLCD.jpg",&BackgpPicture, &BackhmWidth, &BackhmHeight);
      if (!BackgpPicture) mylog("Lost the Picture 1","So cant display it");

    /* The class is registered, let's create the window */
    hwnd = CreateWindow (
           szClassName,         /* Classname */
           NULL,
//           WS_POPUPWINDOW | WS_VISIBLE,
           WS_POPUPWINDOW,
           x,                   /* Windows decides the position */
           y,                   /* where the window ends up on the screen */
           width,               /* The programs width */
           height,              /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );
    if (!hwnd) { mylog_error("Error from CreateWindowEx"); return 0; }

    display_active=1;
    /* Make the window visible on the screen */
    if (pDontShowWindow()==0) ShowWindow (hwnd, SW_NORMAL);
   }
  return 1;
}

// =============================================================================
//
//  This function is called by the Windows function DispatchMessage()
//  Currently only handles the destroy message 
//
// =============================================================================
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{  
//    mylogw("WindowProc Message",message);
    switch (message)                  // handle the messages 
    {
        case WM_DESTROY:
        case WM_ERASEBKGND:
             return 0;
        default:
             return DefWindowProc (hwnd, message, wParam, lParam);
             return 0;
    }

    return 0;
}

// =============================================================================
//
// Check if any new windows messages but without waiting for them
// called whenever a screen refresh occurs
//
// =============================================================================
int check_messages()
{
    /* Run the message loop. It will run until PeekMessage() returns 0 */
    while (PeekMessage (&messages, hwnd, 0, 0, PM_REMOVE))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
     }
}

int overlap(RECT box1, RECT box2)
{  int result, minx,maxx,miny,maxy;

   result=0;
    minx = max(box1.left, box2.left);
    maxx = min(box1.right, box2.right);
    miny = max(box1.top, box2.top);
    maxy = min(box1.bottom, box2.bottom);
    if ((minx <= maxx) && (miny <= maxy)) result=1;
    return result;
}

// =============================================================================
//
// Paint the screen window if it has changed
//
// =============================================================================
int display_screen(void)
{
  FILE *fp;
  char line[80], str[256];
  HFONT hFont, hOldFont;
  HDC mhdc; 
  TRIVERTEX vertex[2] ;
  int i,x,y,width,height;
  PAINTSTRUCT lpPaint;
  HBITMAP hbm,oldhbm;
  RECT rc,vc;
  static RECT lvc;
  double EndTime,Tdiff;

  // Check for any new windows messages
  check_messages();       
  // check any of the display lines have changed
  for (i=0;i<DISPLAY_HEIGHT;i++) 
    { if (strncmp(display[i].line,last_display[i].line,strlen(display[i].line))!=0) 
        { display_changed=1; 
          strcpy(last_display[i].line,display[i].line);
        }
    }

  // get the window params from the ini file that define the window details
  get_win_vals(&x,&y,&width,&height,myscreen);
//  GetWindowRect(hwnd, &rc);                               // returns co-ordinates on screen into rc
  vc.left=GetSystemMetrics(SM_XVIRTUALSCREEN);            // bounding screen size for multiple monitors
  vc.top=GetSystemMetrics(SM_YVIRTUALSCREEN);
  vc.right=vc.left+GetSystemMetrics(SM_CXVIRTUALSCREEN);
  vc.bottom=vc.top+GetSystemMetrics(SM_CYVIRTUALSCREEN);
  // Check if virtual screen dimensions have changed
  if ((lvc.left!=vc.left) || (lvc.right!=vc.right) || (lvc.top!=vc.top) || (lvc.bottom!=vc.bottom)) display_changed=1;
  lvc.left=vc.left;  lvc.right=vc.right;  lvc.top=vc.top;  lvc.bottom=vc.bottom;
//          sprintf(str,"overlap=%d,x=%d, y=%d, rc.left=%d, rc.top=%d, rc.right=%d, rc.bottom=%d, vc.left=%d, vc.top=%d, vc.right=%d, vc.bottom=%d",
//                overlap(rc,vc),x,y,rc.left,rc.top,rc.right,rc.bottom,vc.left,vc.top,vc.right,vc.bottom);  
//          mylog("display_screen",str);   

  if ( (display_active==1) && (display_changed==1) )
    { display_changed=0;
//      sprintf(str,"x=%d, y=%d, vc.left=%d, vc.top=%d, vc.right=%d, vc.bottom=%d",x,y,vc.left,vc.top,vc.right,vc.bottom); 
//      mylog("display_screen moved",str);   
      if (x<vc.left) x=0;
      if (y<vc.top)  y=0;
      MoveWindow(hwnd,x,y,width,height,FALSE); // where we want to be is available inside the virtual screen
//          sprintf(str,"vc.left=%d, vc.top=%d, vc.right=%d, vc.bottom=%d",vc.left,vc.top,vc.right,vc.bottom); 
//          mylog("Virtual screen is",str);             
      // Check for messages caused by the move
      check_messages();
      GetWindowRect(hwnd, &rc);                               // returns co-ordinates on screen into rc
      // get the device contect for the current window
      InvalidateRect(hwnd,NULL,FALSE);          // Make sure the screen refreshes
      hdc=BeginPaint(hwnd,&lpPaint);
      if (!hdc) { mylog_error("Error from BeginPaint"); return 0; }
       
      mhdc = CreateCompatibleDC(hdc); 
      hbm = CreateCompatibleBitmap ( hdc, width, height );
      oldhbm = (HBITMAP) SelectObject(mhdc,hbm);
         
      // output the background picture
      if (!(BackgpPicture)) mylog("Lost the Picture","So cant display it");
	  display_picture(mhdc,&BackgpPicture, &rc, (LONG)0, (LONG)0,(LONG)0, (LONG)0, BackhmWidth, BackhmHeight);

      // Display the text strings for the four lines  
      for (i=0;i<4;i++) { OutputALine(i,mhdc); }

      // Copy the memory DC to the screen then release the memory objects
      BitBlt(hdc,0,0,width,height,mhdc,0,0,SRCCOPY);
      hbm = (HBITMAP) SelectObject(mhdc,oldhbm);
      if ((hbm==NULL) || (hbm==HGDI_ERROR)) { mylog_error("Error from SelectObject(hbm)"); }

      if (DeleteObject(hbm)==0) { mylog_error("Error from DeleteObject(hbm)"); }
      if (DeleteObject(mhdc)==0) { mylog_error("Error from DeleteObject(mhdc)"); }
      EndPaint(hwnd,&lpPaint);
      if (pDontShowWindow()==0) ShowWindow (hwnd, SW_RESTORE);
    }        

  // If we want to capture an image of the screen check if it's time and do it if it is.
  if ( pRecord()!=0 )
    { EndTime=(double)clock()/CLOCKS_PER_SEC;       // Counter time
      Tdiff=EndTime-CaptureCounter;                     // in seconds
      if (Tdiff>pRecordInterval()) { CaptureCounter=EndTime; CaptureAnImage(hwnd); }
    }

  check_messages();
  return 1;
}


// =============================================================================
//
// Function to create a font structure based on the font attributes passed
//
// =============================================================================
HFONT GetAFont(int f1, int f2, int f3, long height, long width, int FontType, char *FontName) 
{ 
    static LOGFONT lf;  // structure for font information  
 
    // Get a handle to the ANSI fixed-pitch font, and copy 
    // information about the font to a LOGFONT structure. 
    if (FontType==0)
      { GetObject(GetStockObject(ANSI_FIXED_FONT), sizeof(LOGFONT), &lf); 
        strcpy(lf.lfFaceName,FontName);
      }
    else
      { GetObject(GetStockObject(FontType), sizeof(LOGFONT), &lf); }
 
    // Set the font attributes, as appropriate.  
    lf.lfWeight = f1;                // 0 to 100
    lf.lfItalic = (f2 == ITALIC);    // 0 or 1
    lf.lfUnderline = (f3 == ULINE);  // 0 or 1
 
    // Create the font, and then return its handle.  
    return CreateFont(height, width, 
        lf.lfEscapement, lf.lfOrientation, lf.lfWeight, 
        lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet, 
        lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality, 
        lf.lfPitchAndFamily, lf.lfFaceName); 
} 



// =============================================================================
//
// Function to Output a a block of characters to the display
//
// =============================================================================
extern void __stdcall OutputChars(int i,int start,int end, HDC mhdc)
{ int j,len;
  char line[80], str[80];
  HFONT hFont, hOldFont;
  UINT fuOptions;
  int widths[80];
  SIZE mySize;

  for (j=0;j<80;j++) widths[j]=0;
  len=end-start+1;
  strncpy(line,&display[i].line[start],len);
  line[len]=0;
//  sprintf(str,"start=%d, end=%d, len=%d, line=%s, x=%d, y=%d",start,end,len,line,myscreen[i].x,myscreen[i].y);
//  mylog("OutputChars",str);
  
  SetTextColor(mhdc, RGB(myscreen[i].r,myscreen[i].g,myscreen[i].b)); 
  // If the background is set to 0,0,0 then make it transparent 
  if ((myscreen[i].rb==0) && (myscreen[i].gb==0) && (myscreen[i].bb==0)) 
    { SetBkMode(mhdc, TRANSPARENT); fuOptions=ETO_CLIPPED;}
  else { SetBkMode(mhdc, OPAQUE); 
         SetBkColor(mhdc, RGB(myscreen[i].rb,myscreen[i].gb,myscreen[i].bb)); fuOptions=ETO_OPAQUE; 
       }

  // set up the font and it's attributes
  hFont = GetAFont(myscreen[i].Bold,myscreen[i].Italics,myscreen[i].Underline,
                   myscreen[i].FontHeight, myscreen[i].FontWidth,myscreen[i].FontType,myscreen[i].FontName);
  // save the old font
  hOldFont = (HFONT)SelectObject(mhdc, hFont);
  // Output the text
  GetTextExtentExPoint(mhdc, line, strlen(line),1000, NULL, widths, &mySize);
  ExtTextOut(mhdc, myscreen[i].x,myscreen[i].y, fuOptions, NULL, line, strlen(line), NULL); 
  // Need to calculate the next character position
  myscreen[i].x=myscreen[i].x+widths[strlen(line)-1];
  
  sprintf(str,"new x=%d,len=%d,(%d,%d,%d,%d)",myscreen[i].x,strlen(line),widths[0],widths[1],widths[2],widths[3]);
//  mylog("OutputChars",str);

  // set back to the old font
  SelectObject(mhdc, hOldFont);
  // and delete the one we created
  if (DeleteObject(hFont)==0) { mylog_error("Error from DeleteObject(hFont)"); }
}


// =============================================================================
//
// Function to Output a single Custom Character
//
// =============================================================================
extern void __stdcall OutputCustomChar(int ln, VLCD_BYTE c, HDC mhdc)
{ char str[80];
  int x,y,width,height,pixsz,piysz;
  int i,j,cn,found;
  RECT myrect;
  HBRUSH myBrush;
  
  cn=c-1; // if (cn==8) cn=0;
  // What's the current text height?
  x=myscreen[ln].x;
  y=myscreen[ln].y;
  height=myscreen[ln].FontHeight;
  width=myscreen[ln].FontWidth;
//  sprintf(str,"line nr.=%d, character code=%d, x=%d, y=%d, width=%d, height=%d",ln,c,x,y,width,height);
//  sprintf(str,"Line nr. %d, code %d, dots %d,%d,%d,%d,%d,%d,%d,%d",ln,c,CustomChars[cn].ln[0],CustomChars[cn].ln[1],
//                        CustomChars[cn].ln[2],CustomChars[cn].ln[3],CustomChars[cn].ln[4],CustomChars[cn].ln[5],
//                        CustomChars[cn].ln[6],CustomChars[cn].ln[7]);
//  mylog("OutputCustomChar",str);
  pixsz=(width/8)+1;
  piysz=(height/10)+1;
  // Check if this custom character is overriden by an icon file
  found=0;
  for (i=0; i<MAX_CMATCH; i++)
    { if (charmap[i].used!=0)
        { found=1; 
          for (j=0;j<8;j++) { if (charmap[i].px[j]!=CustomChars[cn].ln[j]) {found=0; break; } }
          if (found==1) {found=i+1; break;}
        }
    }
//  sprintf(str,"found=%d, cmap=%d,%d,%d,%d,%d,%d,%d,%d",found,CustomChars[cn].ln[0],CustomChars[cn].ln[1]
//                             ,CustomChars[cn].ln[2],CustomChars[cn].ln[3],CustomChars[cn].ln[4]
//                             ,CustomChars[cn].ln[5],CustomChars[cn].ln[6],CustomChars[cn].ln[7]);
//  mylog("CC",str);

  if (found==0)                         // didn't find one
    { // Draw little boxes for each dot set in c
      myBrush=CreateSolidBrush(RGB(myscreen[ln].r,myscreen[ln].g,myscreen[ln].b));
      for (i=0;i<8;i++)
        { for (j=1;j<6;j++)
            { 
//              sprintf(str,"c=%d, j=%d, CustCharline=%d, bitmap line=%d",c,j,CustomChars[c-1].ln[i],bitnum[j-1]);
//              mylog("OutputCustomChar",str);        
              if ( ((CustomChars[cn].ln[i])&(bitnum[j-1]))!=0 ) 
               { myrect.left=x+(width*j)/8;      myrect.top=y+((height*i)/10)+(height/10);
                 myrect.right=myrect.left+pixsz; myrect.bottom=myrect.top+piysz;
//                 sprintf(str,"dot at left=%d, right=%d, top=%d, bottom=%d",myrect.left,myrect.right,myrect.top,myrect.bottom);
//                 mylog("OutputCustomChar",str);
                 FillRect( mhdc, &myrect, myBrush);
               }
            }
        }
      DeleteObject(myBrush); 
      myscreen[ln].x=myscreen[ln].x+(width); // Leave a gap of one pixsz before and 2 after
    }
  else                                      // otherwise draw the image at x,y,width,height
    { found=found-1;
//      mylog("CC","draw Icon");              // draw icon found (1-20) 
      if (charmap[found].loaded==0)
       { // Load the image
         charmap[found].loaded=1;
         LoadPictureFile(charmap[found].fname, &charpictures[found], &charmap[found].width, &charmap[found].height);
       }
      myrect.left=x; myrect.top=y; myrect.right=myrect.left+width; myrect.bottom=myrect.top+height;
      display_picture(mhdc, &charpictures[found], &myrect, x, y, width, height, charmap[found].width, charmap[found].height);   
      myscreen[ln].x=myscreen[ln].x+(width); 
    }
}
  
  
// =============================================================================
//
// Function to Output a line to the display
//
// =============================================================================
extern void __stdcall OutputALine(int i, HDC mhdc)
{ int j,k,ch,chars_waiting;;
  VLCD_BYTE c;
  char str[512],strs[6];
  
//  sprintf(str,"line %d - ",i);
//  for (ch=0;ch<strlen(display[i].line);ch++) {sprintf(strs,"%2d ",(VLCD_BYTE)display[i].line[ch]); strcat(str,strs); }
//  mylog("OutputALine",str);
  j=0;  
  chars_waiting=0;                                     // Current start position
  for (k=0; k<strlen(display[i].line); k++)
    {  c=(VLCD_BYTE)display[i].line[k];
       if ((c>0) && (c<9) || (c==255) || (c==176) || (c==158) || ((c>130) && (c<137)))
         { //if ((k>0) && (k>(j+1))) { OutputChars(i,j,k-1,mhdc); j=k; }
           if (chars_waiting!=0) { OutputChars(i,j,k-1,mhdc); chars_waiting=0; }
           switch (c)
             { case 255: c=9; break; //extra entry in the charmap
               case 136: c=8; break;
               case 176: c=1; break;
               case 158: c=2; break;
               case 131: c=3; break;
               case 132: c=4; break;
               case 133: c=5; break;
               case 134: c=6; break;
               case 135: c=7; break;
             }
           OutputCustomChar(i,c,mhdc);
           j=k+1;
         }
       else chars_waiting=1;
    }
    // Are there any remaining characters to be output?
    if (chars_waiting!=0)  OutputChars(i,j,strlen(display[i].line)-1,mhdc);
}







// =============================================================================
// =============================================================================
//
// Now the DLL entry points
//
// =============================================================================
// =============================================================================

// =============================================================================
// +-----------------------+
// ! DISPLAYDLL_DriverName !
// +-----------------------+
//! Returns the driver name.
//! The driver name is used by the driver selection GUI to identify the driver.
//! It's good practice to return a proper name and version for the driver so that
//! it can be clearly identified (or else we would have to rely on the plugin name
//! which is subject to modifications).
// =============================================================================
DLL_EXPORT(char *) DISPLAYDLL_DriverName(void) 
{ static char plugin_name[80];
  sprintf(plugin_name,"%s %s","VirtualLCD Display v",VER_STRING);
  return plugin_name;
}

// +-----------------+
// ! DISPLAYDLL_Init !
// +-----------------+
//! Initializes the plugin.
//! All initialization stuff goes there: opening and configuring IO ports, device detection and probing...
//! Sets ok to 1 if initialization is successful, 0 otherwise.
//! Returns an error message in case of failure, NULL otherwise.
// =============================================================================
DLL_EXPORT(char *) DISPLAYDLL_Init(VLCD_BYTE size_x,VLCD_BYTE size_y,char *startup_parameters,VLCD_BOOL *ok) 
{ int i;
  
  read_cfg_file();                           // Read in the configuration data
  open_mylog();                              // Open the log file (if it wasn't already open)
  for (i=0;i<DISPLAY_HEIGHT;i++)   strcpy(display[i].line," ");
  strcpy(display[0].line,"VirtualLCD Starting");   // Initialise the display lines
  my_x=1;my_y=1;                             // set the initial display position
  *ok=create_screen();                       // Create the window
  display_screen();                          // and display th data on it
  return DISPLAYDLL_DriverName();            // returning NULL sometimes causes 'DLL may be incompatible'
}

// =============================================================================
// +------------------+
// ! DISPLAYDLL_Usage !
// +------------------+
//! Returns plugin usage hints.
//! The usage text describes the main plugin parameters and how they are assembled
//! in the parameter string (syntax indications).
// =============================================================================
DLL_EXPORT(char *) DISPLAYDLL_Usage(void) {
  static char plugin_usage[]="No parameters required - see VirtualLCD.cfg";
  return plugin_usage;
}

// =============================================================================
// +------------------------------+
// ! DISPLAYDLL_DefaultParameters !
// +------------------------------+
//! Returns the plugin default parameters.
//! The default parameters string is used as parameter string after the driver
//! has been initialized.
// =============================================================================
DLL_EXPORT(char *) DISPLAYDLL_DefaultParameters(void) {
  static char plugin_default_parameters[]=" ";
  return plugin_default_parameters;
}

// =============================================================================
// +------------------------+
// ! DISPLAYDLL_SetPosition !
// +------------------------+
//! Moves the display cursor at a given position.
// =============================================================================
DLL_EXPORT(void) DISPLAYDLL_SetPosition(VLCD_BYTE x,VLCD_BYTE y) 
{ char line[100];
  
  check_messages();   // Check if there are any windows mesages to process
  if (x>DISPLAY_WIDTH) my_x=DISPLAY_WIDTH; else my_x=x; 
  if (y>DISPLAY_HEIGHT)my_y=DISPLAY_HEIGHT; else my_y=y;
}

// =============================================================================
// +------------------+
// ! DISPLAYDLL_Write !
// +------------------+
//! Writes the given string on the display.
//! The string is displayed at the current cursor position (see DISPLAYDLL_SetPosition).
//! It is assumed that the cursor position has been set so that the string can fit.
// =============================================================================
DLL_EXPORT(void) DISPLAYDLL_Write(char *str) 
{
  int i,changed;
  check_messages();  // Check if there are any windows mesages to process
  for (i=0; i<strlen(str); i++) 
    {
            display[my_y-1].line[my_x-1]=str[i];
            if (my_x>(DISPLAY_WIDTH)) 
              { my_x=0; if (my_y>DISPLAY_HEIGHT) my_y=0; my_y++; }
            my_x++;
    }
  display[my_y-1].line[my_x-1]=0;
  display_screen();
}

//==============================================================================
// +-----------------+
// ! DISPLAYDLL_Done !
// +-----------------+
//==============================================================================
DLL_EXPORT(void) DISPLAYDLL_Done(void) 
{ mylog("**** Done Called ****","Closing"); 
  display_active==0; DestroyWindow(hwnd);
  mylog("**** Done Called ****","Closed");
}

//==============================================================================
// +------------------------+
// ! DISPLAYDLL_PowerResume !
// +------------------------+
// From testing it looks like this never gets called
//==============================================================================
DLL_EXPORT(void) DISPLAYDLL_PowerResume(void) 
{  mylog("***** PowerResume *****","Called"); }

//==============================================================================
//
// Unimplemented / Unsupported functions below here
//
//==============================================================================
// +--------------------------+
// ! DISPLAYDLL_SetBrightness !
// +--------------------------+
DLL_EXPORT(void) DISPLAYDLL_SetBrightness(VLCD_BYTE brightness) 
{ mylog("Unimplemented","SetBrightness"); }
// +-----------------------+
// ! DISPLAYDLL_CustomChar !
// +-----------------------+
DLL_EXPORT(void) DISPLAYDLL_CustomChar(VLCD_BYTE chr,VLCD_BYTE *data) 
{ // mylog("Unimplemented","CustomChar"); 
  int i; char str[80];
  
//  sprintf(str,"chr=%d, data=%d,%d,%d,%d,%d,%d,%d,%d",chr,data[0],data[1],data[2],data[3],data[4],data[5],data[6],data[7]);
//  mylog("DISPLAYDLL_CustomeChar",str);
  
  for (i=0; i<8; i++)
    { CustomChars[chr-1].ln[i]=data[i]; }
}
// +----------------------------+
// ! DISPLAYDLL_CustomCharIndex !
// +----------------------------+
DLL_EXPORT(VLCD_BYTE) DISPLAYDLL_CustomCharIndex(VLCD_BYTE index) 
{ // --index; if (index==0) index=8; 
  // mylog("Unimplemented","CustomCharIndex"); 
   char str[80];
   sprintf(str,"index=%d",index);
   mylog("DISPLAYDLL_CustomCharIndex",str);
   return index; 
}
// +--------------------+
// ! DISPLAYDLL_ReadKey !
// +--------------------+
DLL_EXPORT(VLCD_WORD) DISPLAYDLL_ReadKey(void) 
{ // mylog("Unimplemented","ReadKey");
  // This function is called regularly so we can use it to check for changes
  display_screen(); 
  return 0; 
}
// +-------------------------+
// ! DISPLAYDLL_SetBacklight !
// +-------------------------+
DLL_EXPORT(void) DISPLAYDLL_SetBacklight(VLCD_BOOL light_on) 
{ mylog("Unimplemented","SetBackLight"); }
// +------------------------+
// ! DISPLAYDLL_SetContrast !
// +------------------------+
DLL_EXPORT(void) DISPLAYDLL_SetContrast(VLCD_BYTE contrast) 
{ mylog("Unimplemented","SetContrast"); }

// +-------------------+
// ! DISPLAYDLL_SetGPO !
// +-------------------+
DLL_EXPORT(void) DISPLAYDLL_SetGPO(VLCD_BYTE gpo,VLCD_BOOL gpo_on) 
{ mylog("Unimplemented","SetGPO"); }
// +-------------------+
// ! DISPLAYDLL_SetFan !
// +-------------------+
DLL_EXPORT(void) DISPLAYDLL_SetFan(VLCD_BYTE t1,VLCD_BYTE t2) 
{ mylog("Unimplemented","SetFan"); }



